package fractal.fractal;

import jinyilw.common.SettingTools;
import jinyilw.swing.ColorTools;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

public class NewtonFractal2 extends Fractal
{
	private static final int RAND_MAX = 0x7fff;
	private static final int maxColorRoundValue = 512;
	private static final int[] colorRoundTable = ColorTools.getColorRoundTable();
	private static final int[] ExtractNumberCountList = {3, 3, 3, 3, 4, 4, 5, 5, 6, 7,
			8};
	private double m_kR; // 颜色变化强度
	private double m_kG;
	private double m_kB;
	private double m_R0; // 初始颜色
	private double m_G0;
	private double m_B0;
	private double colorK1; // 颜色配比
	private double colorK2;
	private double colorK3;

	private boolean isExtract3Ex;
	private int extractNumber;
	private boolean isTanRev;
	private int iteratInc;

	private int rand = 32000;
	private double colorK = 0.5;

	@Override
	public void initialise()
	{
		rand = (int) (Math.random() * RAND_MAX);
		updateParameter(rand, colorK);
		xmin = -1.7;
		ymin = -1.45;
		dxy = 3.5 / width;
		R = 8;
		setAngle(0);
		setSff(1);
	}

	public void calculatePartFractal2(int r1, int r2, int c1, int c2)
	{
		int rm;
		double[] dLs;
		double x, y, xi, yi, ddxy;
		double xx, yy, yysi, yyco;
		yi = ymin + dxy * r1;
		for (int row = r1; row < r2; row++)
		{
			yy = yi - rotateY;
			yysi = yy * rotateSin;
			yyco = yy * rotateCos;
			rm = row * width;
			ddxy = xDxys[row];
			xi = xMins[row] + ddxy * c1;
			for (int con = c1; con < c2; con++)
			{
				xx = xi - rotateX;
				x = rotateX + xx * rotateCos - yysi;
				y = rotateY + xx * rotateSin + yyco;
				if (isExtract3Ex)
					dLs = getExtractByNewton_3Ex(x, y, iteratInc);
				else
					dLs = getExtractByNewton(x, y, extractNumber, iteratInc,
							isTanRev);
				pixels[rm + con] = getColorRGB(dLs[0], dLs[1], dLs[2]);
				xi += ddxy;
			}
			yi += dxy;
		}
	}

	@Override
	public void calculatePartFractal(int r1, int r2, int c1, int c2)
	{
		if (isXRotate)
		{
			calculatePartFractal2(r1, r2, c1, c2);
			return;
		}
		int rm;
		double[] dLs;
		double x, y, xi, yi;
		double xx, yy, yysi, yyco;
		double yymin = ymin, xxmin = xmin, ddxy = dxy;
		ddxy = ddxy * Math.pow(sff, r1);
		xxmin = xxmin + width * (dxy - ddxy) * 0.5;
		yi = yymin + dxy * r1;
		for (int row = r1; row < r2; row++)
		{
			yy = yi - rotateY;
			yysi = yy * rotateSin;
			yyco = yy * rotateCos;
			rm = row * width;
			xi = xxmin + ddxy * c1;
			for (int con = c1; con < c2; con++)
			{
				xx = xi - rotateX;
				x = rotateX + xx * rotateCos - yysi;
				y = rotateY + xx * rotateSin + yyco;
				if (isExtract3Ex)
					dLs = getExtractByNewton_3Ex(x, y, iteratInc);
				else
					dLs = getExtractByNewton(x, y, extractNumber, iteratInc,
							isTanRev);
				pixels[rm + con] = getColorRGB(dLs[0], dLs[1], dLs[2]);
				xi += ddxy;
			}
			xxmin = xxmin + ddxy * (1 - sff) * 0.5 * width;
			ddxy = ddxy * sff;
			yi += dxy;
		}
	}

	public void updateParameter(int rand, double colorK)
	{
		extractNumber = ExtractNumberCountList[rand % 11];// 用于指定方程的次数！
		isExtract3Ex = (extractNumber == 3) && (rand > (RAND_MAX / 2));// 用于指定方程是否是三次方程！

		colorK3 = colorK2 = colorK;
		colorK1 = colorK2 * colorK2 * colorK2;// 这三个变量有关颜色

		if (rand < (RAND_MAX / 2))
			colorK1 *= -1;
		if (rand < (RAND_MAX / 2))
			colorK2 *= -1;
		if (rand < (RAND_MAX / 2))
			colorK3 *= -1;// 随机正负号！

		double r = 1.0 / (1 << extractNumber - 3);
		r = Math.pow(r, 0.095);// r大概在0.8到1之间
		colorK1 *= r;
		colorK2 *= r;
		colorK3 *= r;

		// 颜色变化强度！
		m_kR = rand * (1.0 / RAND_MAX) * 40 * r + 50 * r; // 40多到90之间的随机浮点数
		m_kG = m_kR;
		m_kB = m_kR;
		// 初始颜色
		m_R0 = rand * (1.0 / RAND_MAX) * maxColorRoundValue; // 0到512之间的随机浮点数
		m_G0 = m_R0;
		m_B0 = m_R0;

		isTanRev = (rand > (RAND_MAX / 4));// 一个概率 真假值
		if (isTanRev)// 用这个来决定 牛顿迭代的循环次数！！！
		{
			if (extractNumber == 3)
				iteratInc = 1 + (rand % 6);
			else
				iteratInc = 1 + (rand % 4);
		} else
			iteratInc = 1 + (rand % 3);
	}

	// 复数四则运算和复角运算。永远都不变的函数
	final double eValue = 0.01;

	// 重写Log函数，用于提高运算速度
	private double mLog(double x)
	{
		if (x < eValue)
			x = Math.pow(x * (1.0 / eValue), 0.3) * eValue;
		return Math.log(x);
	}

	// 平方
	private double sqr(double x)
	{
		return x * x;
	}

	// X的 N次方！
	private double intpow(double x, int N)
	{
		switch (N)
		{
			case 0:
				return 1;
			case 1:
				return x;
			case 2:
				return x * x;
			case 3:
				return x * x * x;
			case 4:
				return sqr(x * x);
			case 5:
				return sqr(x * x) * x;
			case 6:
				return sqr(x * x * x);
		}
		return Math.pow(x, N);
	}

	// 复数平方
	double[] sqr(double x, double y)
	{
		double out_x = x * x - y * y;
		double out_y = 2 * x * y;
		return new double[]{out_x, out_y};
	}

	// 复数乘法
	double[] mul(double x0, double y0, double x1, double y1)
	{
		double out_x = x0 * x1 - y0 * y1;
		double out_y = x0 * y1 + x1 * y0;
		return new double[]{out_x, out_y};
	}

	// 复数的N次方！！！
	double[] pow(double x, double y, int N)
	{
		double out_x, out_y;
		switch (N)
		{
			case 0:
				out_x = 1;
				out_y = 0;
				break;
			case 1:
				out_x = x;
				out_y = y;
				break;
			case 2:
				return sqr(x, y);
			case 3:
			{
				double[] o = sqr(x, y);
				double x1 = o[0], y1 = o[1];
				return mul(x, y, x1, y1);
			}
			case 4:
			{
				double[] o = sqr(x, y);
				double x1 = o[0], y1 = o[1];
				return sqr(x1, y1);
			}
			case 5:
			{
				double[] o = sqr(x, y);
				double x1 = o[0], y1 = o[1];
				double[] o2 = sqr(x1, y1);
				double x2 = o2[0], y2 = o2[1];
				return mul(x, y, x2, y2);
			}
			case 6:
			{
				double[] o = sqr(x, y);
				double x1 = o[0], y1 = o[1];
				double[] o2 = sqr(x1, y1);
				double x2 = o2[0], y2 = o2[1];
				return mul(x1, y1, x2, y2);
			}
			default:
			{
				int half = N >> 1;
				double[] po = pow(x, y, half);
				double xh = po[0], yh = po[1];
				if ((N & 1) == 0)
					return sqr(xh, yh);
				else
				{
					double[] sq = sqr(xh, yh);
					double xsqr = sq[0], ysqr = sq[1];
					return mul(x, y, xsqr, ysqr);
				}
			}
		}
		return new double[]{out_x, out_y};
	}

	// 复数除法
	double[] div(double x0, double y0, double x1, double y1)
	{
		double r = 1 / (x1 * x1 + y1 * y1 + 1e-300);
		double out_x = (x0 * x1 + y0 * y1) * r;
		double out_y = (y0 * x1 - x0 * y1) * r;
		return new double[]{out_x, out_y};
	}

	// 用牛顿法提取 也是三次方
	double[] getExtractByNewton_3Ex(double x0, double y0, long iteratInc)
	{
		x0 *= 0.75;
		y0 *= 0.75;
		// Z^3-1=0
		double x1 = x0, y1 = y0;
		double[] o;
		for (long i = 0; i < iteratInc; ++i)
		{
			x0 = x1;
			y0 = y1;
			o = getNextPos_3Ex(x0, y0);// 第三重循环
			x1 = o[0];
			y1 = o[1];
		}
		double dL1 = mLog(Math.abs(x1 - x0) * Math.abs(y1 - y0)) * 0.6;
		double dL2 = mLog(sqr(x1 - x0) + sqr(y1 - y0)) * 0.6;
		o = getNextPos_3Ex(x1, y1);
		x0 = o[0];
		y0 = o[1];
		double dL3 = mLog(Math.abs(x1 - x0) + Math.abs(y1 - y0)) * 2.0;
		return new double[]{dL1, dL2, dL3};
	}

	// 这是牛顿三次方程的迭代 3Ex表示三次方
	private double[] getNextPos_3Ex(double x0, double y0)
	{
		double x2 = x0 * x0;
		double y2 = y0 * y0;
		double r = (1.0 / 6) / sqr(x2 + y2 + 1e-300);
		double a = x2 - y2;
		double b = x0 * y0 * 2;
		double out_x = -y0 + (a - b) * r;
		double out_y = x0 - (a + b) * r;
		return new double[]{out_x, out_y};
	}

	// 计算下一个坐标的函数
	double[] getNextPos(double x0, double y0, int N, boolean isTanRev)
	{
		// Z^N-1=0
		double seta = isTanRev ? Math.atan2(x0, y0) : Math.atan2(y0, x0);
		//		if (isTanRev)
		//			seta = Math.atan2(x0, y0);
		//		else
		//			seta = Math.atan2(y0, x0);
		double r = Math.sqrt(x0 * x0 + y0 * y0);
		r = r * (N - 1) / N;
		double sl = 1.0 / (N * intpow(r, (N - 1)));
		double out_x = (r * Math.cos(seta) + sl * Math.cos((1 - N) * seta));
		double out_y = (r * Math.sin(seta) + sl * Math.sin((1 - N) * seta));
		return new double[]{out_x, out_y};
	}

	// 这个是 N次方程，也是“用牛顿法提取”，提取出dL1 dL2 dL3三个值。
	double[] getExtractByNewton(double x0, double y0, int N, int iteratInc,
			boolean isTanRev)
	{
		// Z^N-1=0
		double x1 = x0, y1 = y0;
		double[] o;
		for (int i = 0; i < iteratInc; ++i)// 第三重循环
		{
			x0 = x1;
			y0 = y1;
			o = getNextPos(x0, y0, N, isTanRev);// 典型的逃逸时间算法的内层循环。
			x1 = o[0];
			y1 = o[1];
		} // 得到x1 y1和x0 y0的值，用于下面的计算

		double dL1 = mLog(Math.abs(x1 - x0) * Math.abs(y1 - y0)) * 0.6;
		double dL2 = mLog(sqr(x1 - x0) + sqr(y1 - y0)) * 0.6;
		o = getNextPos(x1, y1, N, isTanRev);
		x0 = o[0];
		y0 = o[1];
		double dL3 = mLog(Math.abs(x1 - x0) + Math.abs(y1 - y0)) * 2.0;
		return new double[]{dL1, dL2, dL3};
	}

	// 通过dL1 dL2 dL3得到 相对应的颜色信息，这是最具艺术性的一个函数。
	private int getColorRGB(double dL1, double dL2, double dL3)
	{
		double kR = dL1 * colorK1 + dL2 * colorK2 - dL3 * colorK3;
		double kG = dL1 * colorK1 - dL2 * colorK2 + dL3 * colorK3;
		double kB = -dL1 * colorK1 + dL2 * colorK2 + dL3 * colorK3;

		int red = getColor(m_R0, m_kR, kR);
		int green = getColor(m_G0, m_kG, kG);
		int blue = getColor(m_B0, m_kB, kB);
		return 0xff << 24 | red << 16 | green << 8 | blue;
	}

	protected static int getColor(double Color0, double k, double Gene)
	{
		int rd = (int) (Color0 + k * Gene);
		rd = rd % maxColorRoundValue;
		if (rd < 0)
			rd = -rd;
		return colorRoundTable[rd];
	}

	@Override
	public void writeToDataStream(DataOutputStream dos) throws IOException
	{
		super.writeToDataStream(dos);
		dos.writeInt(rand);
		dos.writeDouble(colorK);
	}

	@Override
	public void readFromDataStream(DataInputStream dis, int version)
			throws IOException
	{
		super.readFromDataStream(dis, version);
		rand = dis.readInt();
		colorK = dis.readDouble();
		updateParameter(rand, colorK);
	}

	@Override
	public void putCustomData()
	{
		super.putCustomData();
		SettingTools.putDefaultSettingData("rand", rand);
		SettingTools.putDefaultSettingData("colorK", colorK);
	}

	@Override
	public void getCustomData()
	{
		super.getCustomData();
		rand = (Integer) SettingTools.getDefaultSettingData("rand", rand);
		colorK = (Double) SettingTools.getDefaultSettingData("colorK", colorK);
		updateParameter(rand, colorK);
	}
}
